package BlockChain

import (
	"math/big"
	"crypto/sha256"
	"fmt"
	"bytes"
)

const targetBit = 8

type ProofOfWork struct {
	Block  *Block   //当前要验证的区块
	diff *big.Int //大数据存储
}

//创建新的工作量证明
func NewProofOfWork(block *Block) *ProofOfWork {
	//设置好难度
	diffBig := big.NewInt(1)
	diff := diffBig.Lsh(diffBig, 256-targetBit)

	return &ProofOfWork{block, diff}
}

//验证
func (proofOfWork *ProofOfWork) IsValid() bool {
	var hashInt big.Int
	hashInt.SetBytes(proofOfWork.Block.Hash)
	if proofOfWork.diff.Cmp(&hashInt) == 1 {
		return true
	}
	return false
}

//数据拼接，返回字节数组
func (pow *ProofOfWork) prepareData(nonce int) []byte {
	data := bytes.Join([][]byte{
		pow.Block.PrevHash,
		pow.Block.Data,
		Int64ToBytes(pow.Block.Timestamp),
		Int64ToBytes(int64(targetBit)),
		Int64ToBytes(int64(nonce)),
		Int64ToBytes(int64(pow.Block.Height)),
	}, []byte{})
	return data
}

func (pow *ProofOfWork) Run() ([]byte, int64) {
	//1. 将Block的属性拼接成字节数组
	nonce := 0
	var hashInt big.Int //存储新生成的HASH
	var hash [32]byte
	for {
		//准备数据
		dataBytes := pow.prepareData(nonce)
		hash = sha256.Sum256(dataBytes)
		fmt.Printf("\r%x :", hash)
		//将hash存储到hashInt
		hashInt.SetBytes(hash[:])
		//fmt.Println(hashInt)
		//判断hashInt是否小于Block里面的target //3. 判断hash有效性,如果满足条件，跳出循环
		if pow.diff.Cmp(&hashInt) == 1 {
			break
		}

		//通过nonce值不断变化求结果
		nonce = nonce + 1
	}
	return hash[:], int64(nonce)
}